最近在學design pattern,看到一個模式很熟悉,原來是曾經在一個爬蟲系統中見過使用這個架構。
這個模式叫 樣板方法模式
通常應用在有一套固定步驟的系統中,
例如,我曾見過的爬蟲系統:
1.開始
2.爬文章
3.整理格式
4.寫入DB
5.結束
就可以把這樣的流程定義在AbstractClass的templateMethod中
結構圖長這樣:
現在以一個煮咖啡因飲料當作範例,
煮飲料的流程是:
首先宣告共同流程的abstract class
public abstract class CaffeineBeverage {
    // templateMethod:定義煮飲料的流程,並宣告為final,避免被subClass override
    final void prepareDrink() {  
        boilWater();
        brew();
        pourInCup();
        if (customerWantsCondiments()) {
            addCondiments();
        }
    }
    // 因應不同飲料有不同的煮法,宣告為abstract,subclass必須override
    abstract void brew();  
    abstract void addCondiments();
    // 所有飲料共通的流程,所以在abstract class這邊就寫邏輯了,但如有需要還是可以被subclass override
    void boilWater() {
        System.out.println("Boiling water.");
    }
    void pourInCup() {
        System.out.println("Pouring into water.");
    }
    // Hook,通常會是空的,可以由subClass決定要不要override
    boolean customerWantsCondiments() { 
        return true;
    }
}
接下來是concreteClass,先是Coffee類別
public class Coffee extends CaffeineBeverage {
    @Override
    void brew() {
        System.out.println("Brew coffee.");
    }
    @Override
    void addCondiments() {
        System.out.println("Add milk.");
    }
}
接下來是另一個concreateClass,NoSugarTea類別
public class NoSugarTea extends CaffeineBeverage {
    @Override
    void brew() {
        System.out.println("Brew no sugar tea.");
    }
    @Override
    void addCondiments() {
        System.out.println("Don't add condiments. This shouldn't be printed.");
    }
    // 這邊有override hook,因為無糖茶不想加料,所以return false
    @Override
    boolean customerWantsCondiments() { 
        return false;
    }
}
最後是client code
public class CaffeineBeverageClient {
    public static void main(String[] args) {
        System.out.println("I would like a cup of coffee.");
        CaffeineBeverage coffee = new Coffee();
        coffee.prepareDrink();
        System.out.println("");
        System.out.println("I would like a cup of no sugar tea.");
        CaffeineBeverage noSugarTea = new NoSugarTea();
        noSugarTea.prepareDrink();
    }
    
    /**
     * output:
     * 
     * I would like a cup of coffee.
     * Boiling water.
     * Brew coffee.
     * Pouring into water.
     * Add milk.
     *
     * I would like a cup of no sugar tea.
     * Boiling water.
     * Brew no sugar tea.
     * Pouring into water.
     * 
     */
}
https://refactoring.guru/design-patterns/template-method
https://www.oreilly.com/library/view/head-first-design/0596007124/